// example: extFun = EvolverLeggo.new(length: 32, evolveDist: 0.2, evolveChance: 0.05, initialValue: 0.3).fn;



EvolverPlugin : AbstractControlPlugin{ 
	
	var steps;
	
	getSpecs{
		^Dictionary[
			\length -> ControlSpec.new( minval:1, maxval:16, step:1),	
			//\evolveDist -> ControlSpec.new( minval:0, maxval:1, step:0.01),	
			//\evolveChance -> ControlSpec.new( minval:0, maxval:1, step:0.01),	
			//\initialValue -> ControlSpec.new( minval:0, maxval:32, step:1),	
			//\startOffset -> ControlSpec.new( minval:0, maxval:32, step:1),	
			\divideLoopLengthBy -> ControlSpec.new( minval:1, maxval:16, step:1),	
			//\intensity -> ControlSpec.new( minval:0, maxval:32, step:1)
		]
	}
	
	populateVals{
		vals.put(\length, 8);
		vals.put(\evolveDist, 0.2);
		vals.put(\evolveChance, 0.1);
		//vals.put(\initialValue, 1);
		vals.put(\startOffset, 0.5);
		vals.put(\divideLoopLengthBy, 1);
		vals.put(\intensity, 1);
	}
	
	onInit{
		steps = List.new(vals.at(\length));
		vals.at(\length).do{ |val|
			steps.add(1);
		}
	}
	
	getLooperLength{
		^(vals.at(\length) * vals.at(\divideLoopLengthBy)).floor; 
	}
	
	getPosition{
		
		^(currentStep + vals.at(\startOffset) ) % vals.at(\length);
	}
	
	growStepsTo{|length|
		(length - steps.size).do{
			steps = steps.add( 1 );	
		}
	}
	
	getStepValue{|position|
		var val;
		val = steps.at(position);
		if( 
			val == nil,
			{
				this.growStepsTo(position + 1);
				^this.getStepValue(position);
			
			},
			{^val}
		)
	}
	
	evolveOrNot{	
		if( (vals.at(\evolveChance) * vals.at(\divideLoopLengthBy)).coin ){
		
			steps.put( 
				this.getPosition, 
				( 
					this.getStepValue(this.getPosition) +  vals.at(\evolveDist).rand2
				).min(1).max(0) 
			);
		};
	}
	
	process{| ctrlVal |
		var reduceVal;
		
		this.evolveOrNot;
						
		currentStep = currentStep + 1;	// increment current step and reduce to zero if we're at the end
		
		if(currentStep >= this.getLooperLength){
			currentStep = 0;	
		};
					
		reduceVal = this.getStepValue(this.getPosition);
		
		reduceVal = reduceVal + (
				(1 - reduceVal) * (1 - vals.at(\intensity) )
			);
		
		^ctrlVal * reduceVal;
			
	}

}
